﻿using Devonline.AspNetCore;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.SignalR;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Logging;

namespace Devonline.Communication;

/// <summary>
/// 针对客户端 TClient 传输类型 T 的通用基类
/// </summary>
/// <typeparam name="TClient"></typeparam>
/// <typeparam name="T"></typeparam>
public abstract class CommunicationCenter<TClient, T> : Hub<TClient> where TClient : class, ICommunicationClient<T>
{
    protected readonly ILogger<CommunicationCenter<TClient, T>> _logger;
    protected readonly IDistributedCache _cache;
    protected readonly HttpContext _httpContext;
    protected readonly HostSetting _hostSetting;

    /// <summary>
    /// 构造方法
    /// </summary>
    /// <param name="logger">日志</param>
    /// <param name="cache">缓存</param>
    /// <param name="httpContextAccessor">http上下文</param>
    /// <param name="hostSetting">设置</param>
    public CommunicationCenter(
        ILogger<CommunicationCenter<TClient, T>> logger,
        IDistributedCache cache,
        IHttpContextAccessor httpContextAccessor,
        HostSetting hostSetting
        )
    {
        if (httpContextAccessor.HttpContext == null)
        {
            throw new ArgumentNullException(nameof(httpContextAccessor.HttpContext), "HttpContext can't be null!");
        }

        _httpContext = httpContextAccessor.HttpContext;
        _logger = logger;
        _cache = cache;
        _hostSetting = hostSetting;
    }

    /// <summary>
    /// 连接中的默认客户端
    /// </summary>
    protected string? ClientId => _httpContext.GetContextOption<string>(_hostSetting.ClientId) ?? _httpContext.GetUserId();
    /// <summary>
    /// 连接中的默认接收者
    /// </summary>
    protected string? Receiver => _httpContext.GetContextOption<string>(_hostSetting.Receiver);

    /// <summary>
    /// 客户端连接到服务器时
    /// </summary>
    /// <returns></returns>
    public override async Task OnConnectedAsync()
    {
        if (string.IsNullOrWhiteSpace(ClientId))
        {
            _logger.LogError("There has no identity in client connection!");
            return;
        }

        //记录当前客户端和客户端连接的对应关系
        _logger.LogWarning("The client {clientId} connected with connectionId: {connectionId}", ClientId, Context.ConnectionId);
        await Groups.AddToGroupAsync(Context.ConnectionId, ClientId);
        await base.OnConnectedAsync();
    }
    /// <summary>
    /// 客户端从服务器断开时
    /// </summary>
    /// <param name="exception"></param>
    /// <returns></returns>
    public override async Task OnDisconnectedAsync(Exception? exception)
    {
        if (string.IsNullOrWhiteSpace(ClientId))
        {
            _logger.LogError("There has no identity in client connection");
            return;
        }

        //移除当前用户和客户端连接的对应关系
        _logger.LogWarning("The client {clientId} disconnected with connectionId: {connectionId}", ClientId, Context.ConnectionId);
        await Groups.RemoveFromGroupAsync(Context.ConnectionId, ClientId);
        await base.OnDisconnectedAsync(exception);
    }

    /// <summary>
    /// 发送数据到默认接收者
    /// </summary>
    /// <param name="t">数据</param>
    /// <returns></returns>
    public virtual async Task Send(T t) => await SendTo(t, Receiver);
    /// <summary>
    /// 直接数据转发
    /// </summary>
    /// <param name="t">数据</param>
    /// <param name="receiver">接收者</param>
    /// <returns></returns>
    public virtual async Task SendTo(T t, string? receiver)
    {
        if (string.IsNullOrWhiteSpace(receiver))
        {
            _logger.LogError("Receiver must not be null!");
            return;
        }

        try
        {
            //发送消息到接收者
            await Clients.Group(receiver).Receive(t);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "The client {clientId} Send message {message} to receiver {receiver} throw exception!", ClientId, t.ToJsonString(), receiver);
        }
    }
}